/**
 * @file .
 * @author wangxiaowei0303@163.com
 */

#ifndef org_wxw0303_bishe_src_Algorithm_h_
#define org_wxw0303_bishe_src_Algorithm_h_

/*
 * NoiseAlgorithm;是所有加噪,去噪算法的基类,由于加噪,去噪算法具有以下共性:
 * 1. 输入是图像,输出是图像;输入除了图像之外还可能有其他参数.
 * 
 * 在程序中使用加噪,去噪算法:
 *   NoiseAlgorithm *algo = NoiseAlgorithm::create("算法名");
 *   if (algo == 0) 
 *       表明算法不存在;
 *   algo->set("参数名1","参数值");
 *   algo->set("参数名2","参数值");
 *   algo->process(img);
 * 即:
 *   1. 创建指定算法;
 *   2. 参数设置;
 *   3. 最后输入一副图像,进行处理.
 *
 * 编写自己的加噪,去噪算法;
 * 1. 编写相应的算法类,并继承 NoiseAlgorithm;
 * 2. 实现 NoiseAlgorithm 中所有的虚函数.
 * 3. 在算法的源文件中,使用 NOISE_ALGO_REGISTER(算法名,算法类名) 注册算法.
 * 4. 将算法编译,链接成一个单独的 so,并放在指定目录下.
 * 
 * 在程序中使用自己的加噪,去噪算法;
 * 1. 使用 dlopen() 加载对应的算法.
 * 2. 使用 NoiseAlgorithm::create() 创建对应算法的实例.
 */

#include <map>
#include <string>
#include <vector>

#include <opencv2/core/core.hpp>

class NoiseAlgorithm;

typedef NoiseAlgorithm* (*AlgoRegFunc)();

/* AlgoMap;用于保存算法名,算法注册函数的映射. */
typedef std::map<std::string,AlgoRegFunc> AlgoMap;

class NoiseAlgorithm {
public:
    /*
     * 根据算法名 algoname 创建指定的算法实例.
     * 若算法 algoname 不存在(或未被加载),则返回 0.
     */
    static NoiseAlgorithm*
    create(const std::string &algoname);

    /*
     * 获取当前已经加载的算法的算法名.
     * @tparam Container 容器,算法名列表将存放到该容器中,容器要求如下:
     *                   1. 提供 push_back(const std::string &) 函数.
     */
    template <typename Container>
    static void
    algoList(Container &list);

    virtual
    ~NoiseAlgorithm();

    /*
     * 获取算法的帮助文本,并追加到 buf 末尾.
     * 在算法的帮助文本中,应该讲明以下内容:
     * 1. 算法简介,如算法是用来干嘛的.
     * 2. 算法所需要的参数;包括参数名,对参数值的要求,参数的意义.
     */
    virtual void
    help(std::string &buf) const= 0;

    /*
     * 将算法名为 argname 的参数值设置为 argval.
     * 由于在调用该函数时,不知道算法对参数值的要求,所以无法检测 argval 的合法性.
     * 所以该函数内部应该检测算法值的合法性.
     * @throw 当算法值不合法时,会抛出异常.
     */
    virtual void
    set(const char *argname,const char *argval) = 0;

    /*
     * 获取算法名为 argname 的参数的参数值,格式化成字符串之后追加到 buf 末尾.
     * @return 若获取成功,则返回指向参数值字符串的指针,否则返回 0.
     *         当调用该函数之前,buf.size() > 0,则返回的指针 != buf.data(),而是
     *         等于 buf.data() + buf.size(),即参数值第一个字符的地址.
     */
    virtual const char*
    get(const char *argname,std::string &buf) const = 0;
    
    /*
     * 获取算法当前支持的参数的参数名.
     */
    virtual void
    getSupportedArgs(std::vector<std::string> &arglist) const = 0;

    /*
     * 处理图像 img,in place.
     * 若有必要,在调用该函数之前,可能需要保存 img 的一份副本(如 img.clone()).
     * 同 set(),由于不清楚算法支持 img 的格式,所以参数合法性检查由算法自身进行.
     * @throw 若 img 格式不合法,或者其他问题,则抛出异常.
     */
    virtual void
    process(cv::Mat &img) = 0;
public:
    static AlgoMap algos_;   
};

/*
 * 算法注册机理,以及 NoiseAlgorithm::create() 的实现;
 * (PS. 这可是我一个很 coool 的想法.:-));
 * 1. 先从在设计这套机理时,我想象的这套注册机理的样子:
 *    * 在实现一个新的算法时,只需要使用 NOISE_ALGO_REGISTER(algoname,algoclass)
 *      注册一下算法即可.之后就只需要 dlopen() 加载算法,然后
 *      NoiseAlgorithm::create() 创建一个算法实例之后,就可以使用了.
 *    * 由于在主程序中实现了 dlopen() 加载算法库,然后 create() 创建算法实例,
 *      然后使用.因此之后要添加一个新的算法,就只需要实现算法,然后 REGISTER()
 *      注册一下之后,就可以直接启动主程序使用,也就是主程序不需要任何修改.
 * 2. 那么这么 coool 的想法如何实现呢;
 *    * 关键就是 NoiseAlgorithm::create() 如何实现?最简单的一个实现如下:
 *        if (algoname == "算法1")
 *            return new 算法1();
 *        if (algoname == "算法2")
 *            return new 算法2();
 *      但是这样当新增一个算法时,需要修改 create(),追加一个 if 分支.那么能不能
 *      让这个过程自动呢?
 *    * 每一个算法都实现一个注册算法,如下:
 *        void*
 *        algoRegister() 
 *        {
 *            return new 算法1();
 *        }
 *      这样 NoiseAlgorithm::create() 只需要调用算法对应的注册函数即可.因此:
 *    * 需要保存算法的算法名,算法注册函数.这里使用了 std::map 保存.
 *    * 需要在加载算法时,将算法的算法名,算法注册函数追加到 std::map 中,一开始
 *      的想法是将一小段代码放在算法 so 中的 _init 代码段,该段代码就是将算法名,
 *      注册函数保存到 std::map 中.当然在 c++ 中,可以在全局构造一个对象,然后该
 *      对象的构造函数就会放在 _init 段中执行,然后...
 */

/*
 * 该类就是上述所需要的对象,其只有一个功能,就是在其构造函数中将算法名,算法注册
 * 函数保存在 std::map 中.
 */
struct NoiseAlgorithmUtil {
    NoiseAlgorithmUtil(const std::string &name,AlgoRegFunc algoregfunc);
};

#define _REG_FUNCNAME(algoclass)  algoclass ## RegisterFunc

#define NOISE_ALGO_REGISTER(name,aclass) \
    static NoiseAlgorithm*                         \
    _REG_FUNCNAME(aclass) ()             \
    {                                    \
        return new aclass;               \
    }                                    \
                                         \
    NoiseAlgorithmUtil __##aclass##Object__(name,_REG_FUNCNAME(aclass));

template<typename Container>
void
NoiseAlgorithm::algoList(Container &list)
{
    const AlgoMap &algos = NoiseAlgorithm::algos_;
    AlgoMap::const_iterator iter = algos.begin();
    AlgoMap::const_iterator end = algos.end();
    for (; iter != end; ++iter) 
        list.push_back(iter->first);
    return ;
}



#endif /* org_wxw0303_bishe_src_Algorithm_h_  */

 
